/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is Forte for Java, Community Edition. The Initial
* Developer of the Original Code is Sun Microsystems, Inc. Portions
* Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
*/
package org.openide.loaders;
import java.awt.event.KeyEvent;
import java.io.IOException;
import java.lang.ref.*;
import java.util.StringTokenizer;
import java.beans.*;
import java.util.Enumeration;
import java.text.MessageFormat;
import javax.swing.tree.TreeSelectionModel;
import javax.swing.tree.TreePath;
import javax.swing.tree.DefaultTreeSelectionModel;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.ChangeListener;
import javax.swing.event.ChangeEvent;
import javax.swing.ListSelectionModel;
import javax.swing.JButton;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import org.openide.TopManager;
import org.openide.DialogDescriptor;
import org.openide.WizardDescriptor;
import org.openide.loaders.*;
import org.openide.nodes.*;
import org.openide.explorer.view.*;
import org.openide.explorer.ExplorerManager;
import org.openide.filesystems.*;
import org.openide.util.UserCancelException;
import org.openide.util.HelpCtx;
import org.openide.util.Mutex;
import org.openide.util.Utilities;
import org.openide.util.enum.*;
/** Dialog that can be used in create from template.
*
* @author Jaroslav Tulach
* @version
*/
final class TemplateWizard2 extends javax.swing.JPanel
implements DocumentListener, WizardDescriptor.FinishPanel, DataFilter,
PropertyChangeListener, VetoableChangeListener {
/** format to for default package */
private static MessageFormat defaultPackageName;
/** listener to changes in the wizard */
private ChangeListener listener;
/** system reference (FileSystem) */
private Reference system = new WeakReference (null);
/** root node */
private Node rootNode;
/** Creates new form NewFromTemplatePanel */
public TemplateWizard2 () {
initComponents ();
setName (org.openide.util.NbBundle.getBundle(TemplateWizard2.class).getString("LAB_TargetLocationPanelName"));
setBorder (new javax.swing.border.EmptyBorder(new java.awt.Insets(8, 8, 8, 8)));
packagesPanel.setBorder (new javax.swing.border.CompoundBorder(
new javax.swing.border.TitledBorder(org.openide.util.NbBundle.getBundle(TemplateWizard2.class).getString("LAB_SelectPackageBorder")),
new javax.swing.border.EmptyBorder(new java.awt.Insets(8, 8, 8, 8)))
);
rootNode = createPackagesNode ();
packagesPanel.getExplorerManager ().setRootContext (rootNode);
packagesPanel.getExplorerManager ().addPropertyChangeListener (this);
packagesPanel.getExplorerManager ().addVetoableChangeListener (this);
// registers itself to listen to changes in the content of document
packageName.getDocument().addDocumentListener(this);
packageName.unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0));
// registers itself to listen to changes in the content of document
className.getDocument().addDocumentListener(this);
className.unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0));
descriptionLabel.setText (org.openide.util.NbBundle.getBundle(TemplateWizard2.class).getString("LAB_TargetLocationDescription"));
}
/** Preffered size */
public java.awt.Dimension getPreferredSize() {
return TemplateWizard.PREF_DIM;
}
/** Request focus.
*/
public void requestFocus () {
className.requestFocus();
className.selectAll ();
}
/** Creates node that displays all packages.
*/
private Node createPackagesNode () {
return TopManager.getDefault().getPlaces().nodes ().repository(
this
);
}
/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the FormEditor.
*/
private void initComponents () {//GEN-BEGIN:initComponents
namePanel = new javax.swing.JPanel ();
jLabel1 = new javax.swing.JLabel ();
className = new javax.swing.JTextField ();
packagesPanel = new org.openide.explorer.ExplorerPanel ();
packageNamePanel = new javax.swing.JPanel ();
jLabel2 = new javax.swing.JLabel ();
packageName = new javax.swing.JTextField ();
beanTreeView = new org.openide.explorer.view.BeanTreeView ();
descriptionLabel = new javax.swing.JLabel ();
setLayout (new java.awt.BorderLayout (0, 8));
namePanel.setLayout (new java.awt.GridBagLayout ());
java.awt.GridBagConstraints gridBagConstraints1;
jLabel1.setText (java.util.ResourceBundle.getBundle("org/openide/loaders/Bundle").getString("CTL_TemplateClassName"));
gridBagConstraints1 = new java.awt.GridBagConstraints ();
gridBagConstraints1.insets = new java.awt.Insets (0, 0, 0, 8);
gridBagConstraints1.anchor = java.awt.GridBagConstraints.WEST;
namePanel.add (jLabel1, gridBagConstraints1);
className.addFocusListener (new java.awt.event.FocusAdapter () {
public void focusGained (java.awt.event.FocusEvent evt) {
classNameFocusGained (evt);
}
}
);
gridBagConstraints1 = new java.awt.GridBagConstraints ();
gridBagConstraints1.gridwidth = 0;
gridBagConstraints1.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints1.weightx = 1.0;
namePanel.add (className, gridBagConstraints1);
add (namePanel, java.awt.BorderLayout.NORTH);
packagesPanel.setLayout (new java.awt.BorderLayout (0, 8));
packageNamePanel.setLayout (new java.awt.GridBagLayout ());
java.awt.GridBagConstraints gridBagConstraints2;
jLabel2.setText (java.util.ResourceBundle.getBundle("org/openide/loaders/Bundle").getString("CTL_TemplatePackageName"));
gridBagConstraints2 = new java.awt.GridBagConstraints ();
packageNamePanel.add (jLabel2, gridBagConstraints2);
packageName.addFocusListener (new java.awt.event.FocusAdapter () {
public void focusGained (java.awt.event.FocusEvent evt) {
packageNameFocusGained (evt);
}
}
);
gridBagConstraints2 = new java.awt.GridBagConstraints ();
gridBagConstraints2.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints2.insets = new java.awt.Insets (0, 8, 0, 0);
gridBagConstraints2.weightx = 1.0;
packageNamePanel.add (packageName, gridBagConstraints2);
packagesPanel.add (packageNamePanel, java.awt.BorderLayout.SOUTH);
beanTreeView.setPopupAllowed (false);
packagesPanel.add (beanTreeView, java.awt.BorderLayout.CENTER);
packagesPanel.add (descriptionLabel, java.awt.BorderLayout.NORTH);
add (packagesPanel, java.awt.BorderLayout.CENTER);
}//GEN-END:initComponents
private void packageNameFocusGained (java.awt.event.FocusEvent evt) {//GEN-FIRST:event_packageNameFocusGained
packageName.selectAll ();
}//GEN-LAST:event_packageNameFocusGained
private void packageModelChanged (javax.swing.event.ListDataEvent evt) {//GEN-FIRST:event_packageModelChanged
}//GEN-LAST:event_packageModelChanged
private void classNameFocusGained (java.awt.event.FocusEvent evt) {//GEN-FIRST:event_classNameFocusGained
if (
Utilities.getOperatingSystem() == Utilities.OS_SOLARIS ||
Utilities.getOperatingSystem() == Utilities.OS_SUNOS
) {
// does not work on CDE window manager, so better do nothing
return;
}
className.selectAll ();
}//GEN-LAST:event_classNameFocusGained
private void templatesTreeValueChanged (javax.swing.event.TreeSelectionEvent evt) {//GEN-FIRST:event_templatesTreeValueChanged
}//GEN-LAST:event_templatesTreeValueChanged
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JPanel namePanel;
private javax.swing.JLabel jLabel1;
private javax.swing.JTextField className;
private org.openide.explorer.ExplorerPanel packagesPanel;
private javax.swing.JPanel packageNamePanel;
private javax.swing.JLabel jLabel2;
private javax.swing.JTextField packageName;
private org.openide.explorer.view.BeanTreeView beanTreeView;
private javax.swing.JLabel descriptionLabel;
// End of variables declaration//GEN-END:variables
//
// Filter to accept only folders
//
/** Should the data object be displayed or not?
* @param obj the data object
* @return <CODE>true</CODE> if the object should be displayed,
* <CODE>false</CODE> otherwise
*/
public boolean acceptDataObject(DataObject obj) {
return obj instanceof DataFolder;
}
/** Allow only simple selection.
*/
public void vetoableChange(PropertyChangeEvent ev)
throws PropertyVetoException {
if (ExplorerManager.PROP_SELECTED_NODES.equals (ev.getPropertyName ())) {
Node[] arr = packagesPanel.getExplorerManager ().getSelectedNodes ();
if (arr.length > 1) {
throw new PropertyVetoException ("Only single selection allowed", ev); // NOI18N
}
}
}
/** Changes in selected node in packages.
*/
public void propertyChange (PropertyChangeEvent ev) {
if (ExplorerManager.PROP_SELECTED_NODES.equals (ev.getPropertyName ())) {
Node[] arr = packagesPanel.getExplorerManager ().getSelectedNodes ();
if (arr.length == 1) {
DataFolder df = (DataFolder)arr[0].getCookie (DataFolder.class);
if (df != null) {
setTargetFolder (df);
return;
}
}
setTargetFolder ((DataFolder)null);
}
}
/** Fires info to listener.
*/
private void fireStateChanged () {
if (listener != null) {
listener.stateChanged (new ChangeEvent (this));
}
}
//
// Modification of package name or class name
//
public void changedUpdate(final javax.swing.event.DocumentEvent p1) {
if (p1.getDocument () == packageName.getDocument ()) {
SwingUtilities.invokeLater (new Runnable () {
public void run () {
String text = packageName.getText ();
if (text != null) {
setTargetFolder (text);
}
}
});
return;
}
if (p1.getDocument () == className.getDocument ()) {
fireStateChanged ();
}
}
public void removeUpdate(final javax.swing.event.DocumentEvent p1) {
// when deleted => do no looking for folder
// changedUpdate (p1);
if (p1.getDocument () == packageName.getDocument ()) {
SwingUtilities.invokeLater(new Runnable () {
public void run () {
if (packageName.getText ().length () == 0) {
FileSystem fs = (FileSystem)system.get ();
if (fs != null) {
DataFolder df = DataFolder.findFolder (fs.getRoot ());
setTargetFolder (df);
packageName.selectAll ();
}
}
}
});
}
// so just check the name
if (p1.getDocument () == className.getDocument ()) {
fireStateChanged ();
}
}
public void insertUpdate(final javax.swing.event.DocumentEvent p1) {
changedUpdate (p1);
}
//
// Wizard
//
/** Get the component displayed in this panel.
* @return the component
*/
public java.awt.Component getComponent () {
return this;
}
/** Help for this panel.
* @return the help or <code>null</code> if no help is supplied
*/
public org.openide.util.HelpCtx getHelp () {
return new HelpCtx (TemplateWizard2.class);
}
/** Provides the wizard panel with the current data--either
* the default data or already-modified settings, if the user used the previous and/or next buttons.
* This method can be called multiple times on one instance of <code>WizardDescriptor.Panel</code>.
* @param settings the object representing wizard panel state, as originally supplied to {@link WizardDescriptor#WizardDescriptor(WizardDescriptor.Iterator,Object)}
*/
public void readSettings (Object settings) {
TemplateWizard wizard = (TemplateWizard)settings;
className.setText (wizard.getClassName ());
String name = wizard.getPackageName ();
FileSystem fs = wizard.getSystem ();
system = new WeakReference (fs);
requestFocus ();
// set the current target folder.
if (fs != null && name != null) {
FileObject fo = fs.find (name, null, null);
if (fo != null) {
// try it by folder
setTargetFolder (DataFolder.findFolder (fo));
return;
}
}
// or if the folder has not been found by name
setTargetFolder (name);
}
/** Provides the wizard panel with the opportunity to update the
* settings with its current customized state.
* Rather than updating its settings with every change in the GUI, it should collect them,
* and then only save them when requested to by this method.
* Also, the original settings passed to {@link #readSettings} should not be modified (mutated);
* rather, the (copy) passed in here should be mutated according to the collected changes.
* This method can be called multiple times on one instance of <code>WizardDescriptor.Panel</code>.
* @param settings the object representing a settings of the wizard
*/
public void storeSettings (Object settings) {
TemplateWizard wizard = (TemplateWizard)settings;
FileSystem fs = (FileSystem)system.get ();
String name = packageName.getText ();
if (name.equals (defaultPackageName (fs))) {
name = "";
}
wizard.setNameSystem (name, fs);
wizard.setTargetName (className.getText ());
}
/** Test whether the panel is finished and it is safe to proceed to the next one.
* If the panel is valid, the "Next" (or "Finish") button will be enabled.
* @return <code>true</code> if the user has entered satisfactory information
*/
public boolean isValid () {
if (!Utilities.isJavaIdentifier(className.getText ())) return false;
String text = packageName.getText ();
if (text.length () == 0) {
Node[] arr = packagesPanel.getExplorerManager ().getSelectedNodes ();
if (arr.length == 1 && arr[0] == rootNode) {
return false;
}
}
if (text.equals (defaultPackageName ((FileSystem)system.get ()))) {
return true;
}
StringTokenizer tok = new StringTokenizer (text, "."); // NOI18N
while (tok.hasMoreElements ()) {
String pkg = tok.nextToken ();
if (!Utilities.isJavaIdentifier (pkg)) {
return false;
}
}
return true;
}
/** Add a listener to changes of the panel's validity.
* @param l the listener to add
* @see #isValid
*/
public void addChangeListener (ChangeListener l) {
if (listener != null) throw new IllegalStateException ();
listener = l;
}
/** Remove a listener to changes of the panel's validity.
* @param l the listener to remove
*/
public void removeChangeListener (ChangeListener l) {
listener = null;
}
/** Computes a suggestion for a given prefix and
* a list of file objects.
*
* @param node the node to start with
* @param pref prefix
* @param first [0] is the first node that satisfies the suggestion
* @return the longest continuation string for all folders that
* starts with prefix
*/
private static String computeSuggestion (
Node node,
String pref,
Node[] first
) {
Node[] arr = node.getChildren ().getNodes ();
String match = null;
for (int i = 0; i < arr.length; i++) {
String name = arr[i].getName ();
if (name.startsWith (pref)) {
// ok, has the right prefix
if (match == null) {
// first match
match = name;
first[0] = arr[i];
} else {
// find common part of the names
int indx = pref.length ();
int end = Math.min (name.length (), match.length ());
while (indx < end && match.charAt (indx) == name.charAt (indx)) {
indx++;
}
match = match.substring (0, indx);
}
}
}
if (match == null || match.length () == pref.length ()) {
return null;
} else {
return match.substring (pref.length ());
}
}
/** Presets a target folder.
* @param f the folder
* @return true if succeeded
*/
private boolean setTargetFolder (final DataFolder f) {
boolean exact;
Node n;
String name;
if (f != null) {
FileObject fo = f.getPrimaryFile ();
name = fo.getPackageName('.');
StringTokenizer st = new StringTokenizer (name, "."); // NOI18N
try {
FileSystem fs = fo.getFileSystem ();
if (fo.isRoot ()) {
name = defaultPackageName (fs);
}
system = new WeakReference (fs);
Enumeration en = new SequenceEnumeration (
new SingletonEnumeration (fs.getSystemName()),
st
);
n = NodeOp.findPath (rootNode, en);
exact = true;
} catch (FileStateInvalidException ex) {
// invalid state of file system => back to root
n = rootNode;
name = ""; // NOI18N
exact = false;
} catch (NodeNotFoundException ex) {
n = ex.getClosestNode();
DataFolder df = (DataFolder)n.getCookie (DataFolder.class);
if (df != null) {
name = df.getPrimaryFile ().getPackageName ('.');
} else {
name = "";
}
exact = false;
}
} else {
// null folder => use root
n = rootNode;
name = null;
exact = true;
}
// remove listener + do change + add listener
ExplorerManager em = packagesPanel.getExplorerManager ();
em.removePropertyChangeListener (this);
packageName.getDocument ().removeDocumentListener (this);
try {
em.setSelectedNodes (new Node[] { n });
} catch (PropertyVetoException ex) {
throw new InternalError ();
}
packageName.setText (name);
packageName.getDocument ().addDocumentListener (this);
em.addPropertyChangeListener (this);
fireStateChanged ();
return exact;
}
/** Presets a target folder.
* @param f the name of target folder
* @return true if succeeded
*/
private boolean setTargetFolder (final String f) {
Node n = null;
NodeNotFoundException closest = null;
Node[] arr = rootNode.getChildren ().getNodes ();
for (int i = 0; i < arr.length; i++) {
Node root = arr[i];
StringTokenizer st = new StringTokenizer (f, "."); // NOI18N
try {
n = NodeOp.findPath (root, st);
break;
} catch (NodeNotFoundException ex) {
if (closest == null && !st.hasMoreElements ()) {
// a test for !hasMoreElements is here to be sure that
// all tokens has been read, so only the last item
// has not been found
closest = ex;
}
}
}
if (n != null) {
// closest node not used
closest = null;
} else {
if (closest == null) {
// the node has not been even found
return false;
}
// we will select the closest node found
n = closest.getClosestNode ();
}
// remove listener + do change + add listener
ExplorerManager em = packagesPanel.getExplorerManager ();
em.removePropertyChangeListener (this);
// change the text if we want to add suggestion
if (closest != null) {
Node[] first = new Node[1];
final String sugg = computeSuggestion (
closest.getClosestNode (),
closest.getMissingChildName(),
first
);
if (sugg != null) {
packageName.getDocument ().removeDocumentListener (
TemplateWizard2.this
);
packageName.setText (f + sugg);
javax.swing.text.Caret c = packageName.getCaret ();
c.setDot (f.length () + sugg.length ());
c.moveDot (f.length ());
packageName.getDocument ().addDocumentListener (
TemplateWizard2.this
);
}
if (first[0] != null) {
// show the first node that fits
n = first[0];
}
}
// change the node
try {
em.setSelectedNodes (new Node[] { n });
} catch (PropertyVetoException ex) {
throw new InternalError ();
}
// change the selected filesystem
DataFolder df = (DataFolder)n.getCookie (DataFolder.class);
if (df != null) {
try {
FileSystem fs = df.getPrimaryFile ().getFileSystem ();
system = new WeakReference (fs);
} catch (FileStateInvalidException ex) {
}
}
em.addPropertyChangeListener (this);
fireStateChanged ();
return closest == null;
}
/** Creates default package name for given file system.
* @param fs the file system
* @return localized name of default package
*/
private static String defaultPackageName (FileSystem fs) {
if (defaultPackageName == null) {
defaultPackageName = new MessageFormat (DataObject.getString (
"FMT_TemplateDefaultPackageName"
));
}
String n = fs == null ? "" : fs.getDisplayName ();
return defaultPackageName.format (new Object[] { n });
}
}